#pragma once
#include "../common/mq_msg/mq_msg.pb.h"
#include "../common/helper.hpp"
#include "../common/log.hpp"
#include "google/protobuf/map.h"
#include <iostream>
#include <unordered_map>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>


namespace xzt
{
    // 1.定义交换机类
    class Exchange
    {
    public:
        using ptr = std::shared_ptr<Exchange>;
        using args_mapType = google::protobuf::Map<std::string, std::string>;
    public:
        Exchange():_name("default"), _type(ExchangeType::UNKNOWNTYPE), _durable(false), 
                _auto_delete(false), _args() {}

        Exchange(const std::string& name, const ExchangeType& type, bool durable, bool auto_delete, const args_mapType& args)
        : _name(name), _type(type), _durable(durable), _auto_delete(auto_delete), _args(args) {}

        // args存储键值对，在数据库中时，会组织格式化字符串存储：key1=val1&key2=val2...
        void setArgs(const std::string& s_args)
        {
            std::vector<std::string> vs;    // 用于存储分割后的kv键值对
            StringHelper::split(s_args, "&", vs);
            
            for(auto& kv: vs)
            {
                size_t pos = kv.find("=");
                std::string key = kv.substr(0, pos);
                std::string val = kv.substr(pos + 1);
                _args.insert({key, val});
            }
        }

        std::string getArgs()
        {
            std::string result = "";
            for(auto& kv: _args)
            {
                result += kv.first + "=" + kv.second + "&";
            }
            if(result.empty() == false)
            {
                result.pop_back();
            }
            return result;
        }
    public:
        std::string _name;   // 交换机名称
        ExchangeType _type;  // 交换机类型
        bool _durable;       // 是否持久化标志
        bool _auto_delete;   // 是否自动删除标志
        google::protobuf::Map<std::string, std::string> _args;  // 其他参数
    };
    // 2.定义交换机数据持久化管理类 -- 数据存储在sqlite数据库中
    class ExchangeMapper        // 充当中间层
    {
    public:
        using name_ptr_mapType = std::unordered_map<std::string, Exchange::ptr>;
    public:
        ExchangeMapper(const std::string& dbfile): _sql_helper(dbfile) 
        {
            // 若文件所在目录不存在，需要先创建父级目录
            std::string path = FileHelper::parentDirectory(dbfile);
            FileHelper::createDirectory(path);
            // sql声明文件（不存在则创建）
            _sql_helper.open();   // 打开数据库
            createTable();       // 创建数据库表
        }

        bool createTable()
        {
            std::stringstream ss;
            ss << "create table if not exists exchange_table (\
            name varchar(32) primary key, \
            type int, \
            durable int, \
            auto_delete int, \
            args varchar(128));";

            bool ret = _sql_helper.exec(ss.str(), nullptr, nullptr);
            if(ret == false)
            {
                ELOG("ExchangeMapper::createTable() -> 数据库表创建失败");
                abort();
            }
            return ret;
        }

        void removeTable()
        {
            std::stringstream ss;
            ss << "drop table if exists exchange_table;";

            bool ret = _sql_helper.exec(ss.str(), nullptr, nullptr);
            if(ret == false)
            {
                ELOG("ExchangeMapper::removeTable() -> 数据库表删除失败");
                abort();
            }
        }

        bool insert(Exchange::ptr& p_exchange)
        {
            std::stringstream ss;
            ss << "insert into exchange_table values(";

            ss << "'" << p_exchange->_name << "', ";
            ss << p_exchange->_type << ", ";
            ss << p_exchange->_durable << ", ";
            ss << p_exchange->_auto_delete << ", ";
            ss << "'" << p_exchange->getArgs() << "');";

            bool ret = _sql_helper.exec(ss.str(), nullptr, nullptr);
            if(ret == false)
            {
                ELOG("ExchangeMapper::insert() -> 数据库表内数据插入失败");
                abort();
            }
            return ret;
        }

        void remove(const std::string& name)
        {
            std::stringstream ss;
            ss << "delete from exchange_table where name='" << name << "';";

            bool ret = _sql_helper.exec(ss.str(), nullptr, nullptr);
            if(ret == false)
            {
                ELOG("ExchangeMapper::remove() -> 数据库表内数据插入失败");
                abort();
            }
        }

        name_ptr_mapType recovery()
        {
            name_ptr_mapType result{};
            std::string sql = "select name, type, durable, auto_delete, args from exchange_table;";
            bool ret = _sql_helper.exec(sql, _selectCallback, &result);
            return result;
        }
    private:
        static int _selectCallback(void* arg, int col_num, char** row, char** fields)
        {
            auto p_map = static_cast<name_ptr_mapType*>(arg);

            auto p_exchange = std::make_shared<Exchange>();
            p_exchange->_name = row[0];
            p_exchange->_type = static_cast<ExchangeType>(std::stoi(row[1]));
            p_exchange->_durable = static_cast<bool>(row[2]);
            p_exchange->_auto_delete = static_cast<bool>(row[3]);
            if(row[4])
            {
                p_exchange->setArgs(row[4]);
            }

            p_map->insert({p_exchange->_name, p_exchange});
            return 0;   // 表示回调函数成功返回
        }
    private:
        SqliteHelper _sql_helper;
    };
    // 3.定义交换机内存数据管理类
    class ExchangeManager       // 最外层，提供对外接口
    {
    public:
        using ptr = std::shared_ptr<ExchangeManager>;
    public:
        ExchangeManager(const std::string& dbfile): _mapper(dbfile)
        {
            _exchanges = _mapper.recovery();
        }

        // 声明交换机
        bool declareExchange(const std::string& name, const ExchangeType& type, 
                            bool durable, bool auto_delete, 
                            const Exchange::args_mapType& args)
        {
            bool ret = false;
            // 注意判断交换机是否存在和插入的操作期间，必须持续持有锁，否则有线程安全的问题
            std::lock_guard<std::mutex> lk(_mutex);
            // 判断该交换机是否已经存在，若存在则无需重复声明
            if(_exchanges.find(name) != _exchanges.end())
            {
                return true;
            }

            auto p_exchange = std::make_shared<Exchange>(name, type, durable, auto_delete, args);
            
            // 判断是否需要持久化存储
            if(durable == true)
            {
                ret = _mapper.insert(p_exchange);
            }
            ret = _exchanges.insert({name, p_exchange}).second;
            return ret;
        }

        // 删除交换机
        void deleteExchange(const std::string& name)
        {
            std::lock_guard<std::mutex> lk(_mutex);
            // 判断该交换机是否存在，若不存在则无需调用删除接口
            auto iter = _exchanges.find(name);
            if(iter == _exchanges.end())
            {
                return;
            }
            // 若非持久存储，则没有进入数据库删除的必要
            if(iter->second->_durable == true)
            {
                _mapper.remove(name);
            }
            _exchanges.erase(name);
        }

        // 获取交换机
        Exchange::ptr selectExchange(const std::string& name)
        {
            std::lock_guard<std::mutex> lk(_mutex);
            auto iter = _exchanges.find(name);
            if(iter == _exchanges.end())    // 未找到
            {
                return Exchange::ptr();
            }
            return iter->second;
        }

        size_t size()
        {
            std::lock_guard<std::mutex> lk(_mutex);
            return _exchanges.size();
        }

        // 判断交换机是否存在
        bool exists(const std::string& name)
        {
            std::lock_guard<std::mutex> lk(_mutex);
            auto iter = _exchanges.find(name);
            if(iter == _exchanges.end())    // 未找到
            {
                return false;
            }
            return true;
        }

        // 清理所有交换机数据
        void clear()
        {
            std::lock_guard<std::mutex> lk(_mutex);
            _mapper.removeTable();
            _exchanges.clear();
        }

    private:
        std::mutex _mutex;
        ExchangeMapper _mapper;
        ExchangeMapper::name_ptr_mapType _exchanges;
    };
}

